#include "glad/glad.h"
// ------------------

#include "LeWindow.h"
#include "LeInput.h"
#include "LeVBOManager.h"
#include "LePhysicsWorld.h"
#include "LeGui.h"
#include "LeTime.h"
#include "LeScene.h"

//------
#include "LeShader.h"
#include <iostream>
#include "LeScreenSettings.h"
//-------

#define TET

int main()
{
    // initialize window
    // ------------------------------
    LeWindow* window = new LeWindow();
    if(window->initWindow() == -1)
        return -1;

    // configure global opengl state
    // -----------------------------
    window->configureGlobalOpenGLState();

    // initialize LeInput data
    // -----------------------
    LeInput::window = window->getWindow();

    //init VBOManager
    VBOFactory vbofactory;
    
    // Init Physics World
    // ------------------
    LePhysicsWorld::initPhysicsWorld();

    // initialize GameObjects, Awake(), PreRender(), Start() also happen here.
    // -----------------------------------------------------------------------
    LeScene::getInstance()->Initialize();
    
    //---init imgui---
    //----------------
    LeGui gui;
    gui.initGui(window->getWindow());

#ifdef TEST
    // ----test
    LeShader screenShader;
    screenShader.initShaderProgram("/Users/wangle/CLionProjects/Leste/res/shader/postprocessing.vs",
                                   "/Users/wangle/CLionProjects/Leste/res/shader/postprocessing.fs");

    float quadScreenVertices[] = { // vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
            // positions   // texCoords
            -1.0f,  1.0f,  0.0f, 1.0f,
            -1.0f, -1.0f,  0.0f, 0.0f,
            1.0f, -1.0f,  1.0f, 0.0f,

            -1.0f,  1.0f,  0.0f, 1.0f,
            1.0f, -1.0f,  1.0f, 0.0f,
            1.0f,  1.0f,  1.0f, 1.0f
    };

    // screen quad VAO
    unsigned int quadVAO, quadVBO;
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadScreenVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));

    screenShader.useShader();
    screenShader.setInt("screenTexture", 0);

    // framebuffer configuration
    // -------------------------
    unsigned int framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    // create a color attachment texture
    unsigned int textureColorbuffer;
    glGenTextures(1, &textureColorbuffer);
    glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH*2, SCR_HEIGHT*2, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
    // create a renderbuffer object for depth and stencil attachment (we won't be sampling these)
    unsigned int rbo;
    glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCR_WIDTH*2, SCR_HEIGHT*2); // use a single renderbuffer object for both a depth AND stencil buffer.
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it
    // now that we actually created the framebuffer and added all attachments we want to check if it is actually complete now
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    // draw as wireframe
//    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    // --------test

#endif


    
    // render loop
    // -----------
    while (!window->shouldCloseWindow())
    {
        // exit function
        // -----
        if(LeInput::getKey(GLFW_KEY_ESCAPE) == GLFW_PRESS)
            window->setShouldCloseWindow(true);

        // Show Gui window
        //---------------
        gui.startGui();
        gui.showDemoWindow();
//        gui.showGuiWindow();
        gui.showAnotherWindow();
        
        // Update deltaTime
        // ----------------
        LeTime::updateTime();

        // Render Selection
        // ----------------
        window->configureRenderingOpenGL(0.0f, 0.0f, 0.0f, 0.0f);
        LeScene::getInstance()->RenderSelection();

#ifdef TEST
        // Test
        //------------------
        // render
        // ------
        // bind to framebuffer and draw scene as we normally would to color texture
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glEnable(GL_DEPTH_TEST); // enable depth testing (is disabled for rendering screen-space quad)



        //-------

#endif

        LeScene::getInstance()->UpdateCurMouseHoverID();

        // Update
        // ------
        LeScene::getInstance()->Update();

        // world step
        LePhysicsWorld::Step();

        // Post worldstep handle
        LeScene::getInstance()->PostWorldStep();

        // GameObjects Fixedupdate
        // -----------------------
        LeScene::getInstance()->FixedUpdate();

        // destroy gameobjs added to list
        LeScene::getInstance()->DestroyGameObjectsByIDs();


        // TODO: Postprocessing FrameBuffer should be control/ owned by LeWindow Class
        // Postprocessing Setup
        // --------------------
        LeScene::getInstance()->PostprocessingSetup();

        // render
        // ------
//        window->configureRenderingOpenGL(0.5882, 0.7922, 0.9686, 1);
        window->configureRenderingOpenGL(0.0, 0.0, 0.0, 1);
        LeScene::getInstance()->Render();

#ifdef TEST
        // test -------------------
        // now bind back to default framebuffer and draw a quad plane with the attached framebuffer color texture
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDisable(GL_DEPTH_TEST); // disable depth test so screen-space quad isn't discarded due to depth test.
        // clear all relevant buffers
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f); // set clear color to white (not really necessary actually, since we won't be able to see behind the quad anyways)
        glClear(GL_COLOR_BUFFER_BIT);

        screenShader.useShader();
        glBindVertexArray(quadVAO);
        glBindTexture(GL_TEXTURE_2D, textureColorbuffer);	// use the color attachment texture as the texture of the quad plane
        glDrawArrays(GL_TRIANGLES, 0, 6);
        // --------------------------
#endif

        // Gui Rendering
        gui.renderGui();


        window->swapWindowBuffers();
        window->pollWindowEvents();
    }

    LeScene::getInstance()->PostRender();
    gui.shutGui();
    window->terminateWindow();
    return 0;
}
